package base;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

/**
 * 基础的简单API
 *
 * @author yanbin
 * @version 2018/9/3 11:37
 **/
public class BaseApi {

	/**
	 * 连接zookeeper
	 * 
	 * @return
	 * @throws IOException
	 */
	public ZooKeeper connect() throws IOException {

		// 1、指定IP和端口
		String connect = "10.19.37.235:2181";

		// 2、进行连接
		ZooKeeper zk = new ZooKeeper(connect, 2000, null);

		// ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)
		// connectString：以逗号分隔的主机:端口号列表，每个对应一个ZooKeeper服务器。
		// sessionTimeout：这是以毫秒为单位的会话超时时间。这是ZooKeeper在宣布session结束之前，没有从客户端获得心跳的时间。
		// watcher：一个watcher对象，如果创建，当状态改变和发生节点事件时会收到通知。这个watcher对象需要通过一个用户定义的类单独创建，
		// 通过实现Watcher接口并将实例化的对象传递给ZooKeeper构造方法。客户端应用程序可以收到各种类型的事件的通知，例如连接丢失、会话过期等。

		return zk;
	}

	/**
	 * 创建节点：分为同步和异步
	 * 
	 * @param zk
	 * @throws UnsupportedEncodingException
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public void createNode(ZooKeeper zk, String path, String data)
			throws UnsupportedEncodingException, KeeperException, InterruptedException {
		// 同步创建方式：
		// String create(final String path,byte data[],List<ACL> acl,CreateMode model);
		// path 要创建的数据节点的路径
		// data [] 节点创建时初始数据内容
		// acl 节点acl安全策略 :Ids.OPEN_ACL_UNSAFE
		// createMode 创建模式：
		// PERSISTENT 持久；
		// ERSISTENT_SEQUENTIAL 持久顺序；
		// EPHEMERAL临时；连接断了之后就消失
		// EPHEMERAL_SEQUENTIAL 临时顺序
		// cb 回调接口
		// ctx 传递对象，用来在回调方法中使用 通常是个上下文对象
		String result = zk.create(path, data.getBytes("UTF-8"), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		System.out.println(path + ":创建成功...." + result);

		// 异步创建方式：
		// void create(String path,byte data[],List<ACL> acl,CreateMode
		// createMode,StringCallback cb,Object ctx);

		// 注意：如果节点已经存在了，再次创建就会抛出异常，如果父节点不存在就直接创建子节点，也会抛出异常.

	}

	/**
	 * 
	 * @param zk
	 * @param path
	 * @param data
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public void updateNode(ZooKeeper zk, String path, String data) throws KeeperException, InterruptedException {
		// 同步方式
		// Stat setData(String path,byte data[],int version)
		// version可以传入-1，表明要基于最新版本进行更新操作
		Stat stat = zk.setData(path, data.getBytes(), -1);
		System.out.println(path + ":更新成功....");
		// 异步方式
		// void setData(String path,byte data[],int version,StatCallback cb,Object ctx)
	}

	/**
	 * 删除节点
	 * 
	 * @param zk
	 * @param path
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public void deleteNode(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
		// 同步删除：
		// public void delete(String path,int version)
		// version可以传入-1，表明要基于最新版本进行删除操作
		zk.delete(path, -1);
		System.out.println(path + ":删除成功....");
		// 异步删除
		// public void delete(String path,int version,VoidCallback cb,Object ctx)

		// 注意：无法删除存在子节点的节点，即如果要删除一个节点，必须要先删除其所有子节点
	}

	/**
	 * 判断节点是否存在
	 *
	 * @param zk
	 * @param path
	 * @return
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public boolean isExist(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
		Stat stat = zk.exists(path, false);
		if (stat == null) {
			System.out.println("节点 path=" + path + "不存在");
			return false;
		}
		System.out.println("节点 path=" + path + "存在,stat=" + stat);
		return true;
	}

	/**
	 * 获取zookeeper指定路径下的子级Node
	 * 
	 * @param zk
	 * @param path
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public void getNodeList(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
		// getChildren 来获取指定path下的节点
		List<String> listNode = zk.getChildren(path, false);

		if (null == listNode || listNode.size() == 0) {
			return;
		}
		for (String child : listNode) {
			System.out.println(child);
		}
	}

	/**
	 * 获取zookeeper指定路径下的所有节点。通过递归输出
	 * 
	 * @param zk
	 * @param path
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public void getAllNodeList(ZooKeeper zk, String path) throws KeeperException, InterruptedException {

		// getChildren 来获取指定path下的节点
		List<String> listNode = zk.getChildren(path, false);

		if (null == listNode || listNode.size() == 0) {
			return;
		}
		for (String child : listNode) {
			// 拼接真实路径
			String node = (path.equals("/") ? "" : path) + "/" + child;
			System.out.println(node);
			// 递归迭代
			getAllNodeList(zk, node);
		}
	}

	/**
	 * 获取节点下的值
	 * 
	 * @param zk
	 * @param path
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public void getNodeData(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
		// 同步方式
		// byte [] getData(String path,Watcher watcher, Stat stat)
		// byte [] getData(String path,boolean watch, Stat stat)
		Stat stat = new Stat();
		byte[] data = zk.getData(path, false, stat);
		System.out.println(new String(data));

		// 异步方式
		// void getData(String path,Watcher watcher, DataCallback cb,Object ctx)
		// void getData(String path,boolean watch, DataCallback cb,Object ctx)
	}

	/**
	 * 遍历所有节点，并获取他的值
	 * 
	 * @param zk
	 * @param path
	 */
	public void getAllNodeAndData(ZooKeeper zk, String path) throws KeeperException, InterruptedException {
		// getChildren 来获取指定path下的节点
		List<String> listNode = zk.getChildren(path, false);

		if (null == listNode || listNode.size() == 0) {
			return;
		}
		for (String child : listNode) {
			// 拼接真实路径
			Stat stat = new Stat();
			String node = (path.equals("/") ? "" : path) + "/" + child;
			byte[] data = zk.getData(node, false, stat);
			System.out.println(node + "：" + new String(data));
			// 递归迭代
			getAllNodeAndData(zk, node);
		}
	}

}
